// Package main
// Author liuzhiyong
// Date 2023/11/17
// Description 主函数
package main

import (
	"context"
	"flag"
	"fmt"
	"gitee.com/liu_zy_developer/go_stock_service/config"
	"gitee.com/liu_zy_developer/go_stock_service/dao/mysql"
	"gitee.com/liu_zy_developer/go_stock_service/dao/redis"
	"gitee.com/liu_zy_developer/go_stock_service/handler"
	"gitee.com/liu_zy_developer/go_stock_service/logger"
	"gitee.com/liu_zy_developer/go_stock_service/proto"
	"gitee.com/liu_zy_developer/go_stock_service/registry"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"syscall"
)

// go run main.go
func main() {
	var cfn string
	// 0.从命令行获取可能的conf路径
	// stock_service -conf="./conf/config_qa.yaml"
	// stock_service -conf="./conf/config_online.yaml"
	flag.StringVar(&cfn, "conf", "./conf/config.yaml", "指定配置文件路径")
	flag.Parse()
	// 1. 加载配置文件
	err := config.Init(cfn)
	if err != nil {
		panic(err) // 程序启动时加载配置文件失败直接退出
	}
	// 2. 加载日志
	err = logger.Init(config.Conf.LogConfig, config.Conf.Mode)
	if err != nil {
		panic(err) // 程序启动时初始化日志模块失败直接退出
	}
	// 3. 初始化MySQL
	err = mysql.Init(config.Conf.MySQLConfig)
	if err != nil {
		panic(err) // 程序启动时初始化MySQL失败直接退出
	}
	err = redis.Init(config.Conf.RedisConfig)
	if err != nil {
		panic(err)
	}
	// 4. 初始化Consul
	err = registry.Init(config.Conf.ConsulConfig.Addr)
	if err != nil {
		panic(err) // 程序启动时初始化注册中心失败直接退出
	}

	// 5. 创建grpc服务 监听端口, 监听Grpc请求
	// net.Listen("tcp", "127.0.0.1:8381")
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", config.Conf.RpcPort))
	if err != nil {
		panic(err)
	}
	// 创建gRPC服务
	s := grpc.NewServer()
	// 注册健康检查服务，支持consul来对我进行健康检查
	grpc_health_v1.RegisterHealthServer(s, health.NewServer())
	// 库存服务注册RPC服务
	proto.RegisterStockServer(s, &handler.StockSrv{})
	// 启动gRPC服务
	go func() {
		err = s.Serve(lis)
		if err != nil {
			panic(err)
		}
	}()
	// 注册服务到consul
	registry.Reg.RegisterService(config.Conf.Name, config.Conf.IP, config.Conf.RpcPort, nil)

	zap.L().Info(
		"rpc server start",
		zap.String("ip", config.Conf.IP),
		zap.Int("port", config.Conf.RpcPort),
	)

	// 6. grpc-gateway 配置 配置grpc客户端, 启动HTTP服务
	// Create a client connection to the gRPC server we just started
	// This is where the gRPC-Gateway proxies the requests
	conn, err := grpc.DialContext( // RPC客户端
		context.Background(),
		fmt.Sprintf("%s:%d", config.Conf.IP, config.Conf.RpcPort),
		grpc.WithBlock(),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		log.Fatalln("Failed to dial server:", err)
	}

	gwmux := runtime.NewServeMux()
	// 注册HTTP控制器
	err = proto.RegisterStockHandler(context.Background(), gwmux, conn)
	if err != nil {
		log.Fatalln("Failed to register gateway:", err)
	}
	// 创建HTTP服务
	gwServer := &http.Server{
		Addr:    fmt.Sprintf(":%d", config.Conf.HttpPort),
		Handler: gwmux,
	}
	zap.L().Info(
		"gRPC-Gateway HTTP Server start",
		zap.String("ip", config.Conf.IP),
		zap.Int("port", config.Conf.HttpPort),
	)
	// sugar
	zap.S().Infof("Serving gRPC-Gateway on http://0.0.0.0:%d", config.Conf.HttpPort)
	// zap.L().Info(fmt.Sprintf("Serving gRPC-Gateway on http://0.0.0.0:%d", config.Conf.HttpPort))
	// 协程启动HTTP服务
	go func() {
		err := gwServer.ListenAndServe()
		if err != nil {
			log.Printf("gwServer.ListenAndServe failed, err: %v", err)
			return
		}
	}()

	// 服务退出时要注销服务
	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT)
	<-quit // 正常会hang在此处
	// 退出时注销服务
	serviceId := fmt.Sprintf("%s-%s-%d", config.Conf.Name, config.Conf.IP, config.Conf.RpcPort)
	registry.Reg.Deregister(serviceId)
}
